package com.fps.webshop.question.question0001_0099;

import java.util.ArrayList;
import java.util.List;

/**
 * 73. 矩阵置零
 * 
 * 给定一个 m x n 的矩阵，如果一个元素为 0 ，则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。
 * 
 * 进阶：
 * 		一个直观的解决方案是使用  O(mn) 的额外空间，但这并不是一个好的解决方案。
 * 		一个简单的改进方案是使用 O(m + n) 的额外空间，但这仍然不是最好的解决方案。
 * 		你能想出一个仅使用常量空间的解决方案吗？
 * 
 * 示例 1：
 * Question073_01.jpg
 * 输入：matrix = [[1,1,1],[1,0,1],[1,1,1]]
 * 输出：[[1,0,1],[0,0,0],[1,0,1]]
 * 
 * 示例 2：
 * Question073_02.jpg
 * 输入：matrix = [[0,1,2,0],[3,4,5,2],[1,3,1,5]]
 * 输出：[[0,0,0,0],[0,4,5,0],[0,3,1,0]]
 * 
 * 提示：
 * 		m == matrix.length
 * 		n == matrix[0].length
 * 		1 <= m, n <= 200
 * 		-2^31 <= matrix[i][j] <= 2^31 - 1
 */
public class Question073 {
	
	/**
	 * 第一次提交，使用了一个List来记录所有0出现的位置
	 * 但这一题是考空间的，这种解法空间复杂度上并不好
	 * 
	 * 提交结果	执行用时	内存消耗	语言		提交时间				备注
	 * 通过		1 ms	39.9 MB	Java	2021/08/18 17:31
	 * 
	 * 执行用时：1 ms, 在所有 Java 提交中击败了99.03%的用户
	 * 内存消耗：39.9 MB, 在所有 Java 提交中击败了53.87%的用户
	 */
	public static void setZeroes(int[][] matrix) {
		int m = matrix.length;
		int n = matrix[0].length;
		List<int[]> list = new ArrayList<int[]>();
		for(int i=0;i<matrix.length;i++) {
			for(int j=0;j<matrix[i].length;j++) {
				if(matrix[i][j]==0) {
					list.add(new int[] {j,i});
				}
			}
		}
		for(int i = 0;i<list.size();i++) {
			int mm = list.get(i)[0];
			int nn = list.get(i)[1];
			for(int j=0;j<m;j++) {
				matrix[j][mm] = 0;
			}
			for(int j=0;j<n;j++) {
				matrix[nn][j] = 0;
			}
		}
    }
	
	/**
	 * 官方解法1，使用标记数组，分别记录每一行和每一列是否有零出现
	 * 
	 * 提交结果	执行用时	内存消耗	语言		提交时间				备注
	 * 通过		1 ms	40 MB	Java	2021/08/18 17:50
	 * 
	 * 执行用时：1 ms, 在所有 Java 提交中击败了99.03%的用户
	 * 内存消耗：40 MB, 在所有 Java 提交中击败了41.92%的用户
	 */
	public void setZeroes1(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        boolean[] row = new boolean[m];
        boolean[] col = new boolean[n];
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (matrix[i][j] == 0) {
                    row[i] = col[j] = true;
                }
            }
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (row[i] || col[j]) {
                    matrix[i][j] = 0;
                }
            }
        }
    }
	
	/**
	 * 官方解法2，使用两个标记变量
	 * 我们可以用矩阵的第一行和第一列代替方法一中的两个标记数组，以达到 O(1) 的额外空间。
	 * 但这样会导致原数组的第一行和第一列被修改，无法记录它们是否原本包含 0。
	 * 因此我们需要额外使用两个标记变量分别记录第一行和第一列是否原本包含 0。
	 * 在实际代码中，我们首先预处理出两个标记变量，接着使用其他行与列去处理第一行与第一列，
	 * 然后反过来使用第一行与第一列去更新其他行与列，最后使用两个标记变量更新第一行与第一列即可。
	 * 
	 * 提交结果	执行用时	内存消耗	语言		提交时间				备注
	 * 通过		1 ms	39.9 MB	Java	2021/08/18 17:51
	 * 
	 * 执行用时：1 ms, 在所有 Java 提交中击败了99.03%的用户
	 * 内存消耗：39.9 MB, 在所有 Java 提交中击败了44.55%的用户
	 */
	public void setZeroes2(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        boolean flagCol0 = false, flagRow0 = false;
        for (int i = 0; i < m; i++) {
            if (matrix[i][0] == 0) {
                flagCol0 = true;
            }
        }
        for (int j = 0; j < n; j++) {
            if (matrix[0][j] == 0) {
                flagRow0 = true;
            }
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][j] == 0) {
                    matrix[i][0] = matrix[0][j] = 0;
                }
            }
        }
        for (int i = 1; i < m; i++) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
        }
        if (flagCol0) {
            for (int i = 0; i < m; i++) {
                matrix[i][0] = 0;
            }
        }
        if (flagRow0) {
            for (int j = 0; j < n; j++) {
                matrix[0][j] = 0;
            }
        }
    }
	
	/**
	 * 官方解法3，使用一个标记变量
	 * 对方法二进一步优化，只使用一个标记变量记录第一列是否原本存在 0。
	 * 这样，第一列的第一个元素即可以标记第一行是否出现 0。
	 * 但为了防止每一列的第一个元素被提前更新，我们需要从最后一行开始，倒序地处理矩阵元素。
	 * 
	 * 提交结果	执行用时	内存消耗	语言		提交时间				备注
	 * 通过		1 ms	39.9 MB	Java	2021/08/18 17:54
	 * 
	 * 执行用时：1 ms, 在所有 Java 提交中击败了99.03%的用户
	 * 内存消耗：39.9 MB, 在所有 Java 提交中击败了54.57%的用户
	 */
	public void setZeroes3(int[][] matrix) {
        int m = matrix.length, n = matrix[0].length;
        boolean flagCol0 = false;
        for (int i = 0; i < m; i++) {
            if (matrix[i][0] == 0) {
                flagCol0 = true;
            }
            for (int j = 1; j < n; j++) {
                if (matrix[i][j] == 0) {
                    matrix[i][0] = matrix[0][j] = 0;
                }
            }
        }
        for (int i = m - 1; i >= 0; i--) {
            for (int j = 1; j < n; j++) {
                if (matrix[i][0] == 0 || matrix[0][j] == 0) {
                    matrix[i][j] = 0;
                }
            }
            if (flagCol0) {
                matrix[i][0] = 0;
            }
        }
    }
	
	public static void main(String[] args) {
		int[] a1 = new int[] {0,1,2,0};
		int[] a2 = new int[] {3,4,5,2};
		int[] a3 = new int[] {1,3,1,5};
		
		int[][]a = new int[][] {a1,a2,a3};
		
		setZeroes(a);
		
		for(int i=0;i<a.length;i++) {
			for(int j=0;j<a[i].length;j++) {
				System.out.print(a[i][j]+" ");
			}
			System.out.println();
		}
	}
}
